package org.papervision3d.core.math
{
	import org.papervision3d.Papervision3D;
	
/**
* <span lang="ja">Number3D クラスは、3次元座標系の中の値を表わします。x,y,z プロパティは、それぞれ水平、垂直、z 深度の座標軸を表します。</span>
* 
* 
* <span lang="en">The Number3D class represents a value in a three-dimensional coordinate system.
* Properties x, y and z represent the horizontal, vertical and z the depth axes respectively.</span>
*
*/
public class Number3D
{
	/**
	* <span lang="ja">水平座標の値です。</span>
	* <span lang="en">The horizontal coordinate value.</span>
	*/
	public var x: Number;

	/**
	* <span lang="ja">垂直座標の値です。</span>
	* <span lang="en">The vertical coordinate value.</span>
	*/
	public var y: Number;

	/**
	* <span lang="ja">深度座標の値です。</span>
	* <span lang="en">The depth coordinate value.</span>
	*/
	public var z: Number;

	/**
	 * <span lang="ja">Number3D が作成される前に ： 一時的に Number3D を格納する方法として、様々なメソッドによって使用されます。</span>
	 * <span lang="en">pre-made Number3D : used by various methods as a way to temporarily store Number3Ds. </span>
	 */
	static private var temp : Number3D = Number3D.ZERO; 
	
	static public var toDEGREES :Number = 180/Math.PI;
	static public var toRADIANS :Number = Math.PI/180;
	
	/**
	* <span lang="ja">x,y,z 引数によって、3次元の値が指定される、新しい Number3D オブジェクトを作成します。引数なしでこのコンストラクタを呼び出すときは、x,y,z プロパティはゼロで作成されます。</span>
	* 
	* 
	* 
	* <span lang="en">Creates a new Number3D object whose three-dimensional values are specified by the x, y and z parameters. 
	* If you call this constructor function without parameters, a Number3D with x, y and z properties set to zero is created.</span>
	*
	* @param	x	The horizontal coordinate value. The default value is zero.
	* @param	y	The vertical coordinate value. The default value is zero.
	* @param	z	The depth coordinate value. The default value is zero.
	*/
	public function Number3D( x: Number=0, y: Number=0, z: Number=0 )
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}


	/**
	* <span lang="ja">同じ3次元の値で、オリジナルのインスタンスのクローンである、新しい Number3D オブジェクトを取得します。</span>
	* <span lang="en">Returns a new Number3D object that is a clone of the original instance with the same three-dimensional values.</span>
	*
	* @return	A new Number3D instance with the same three-dimensional values as the original Number3D instance.
	*/
	public function clone():Number3D
	{
		return new Number3D( this.x, this.y, this.z );
	}
	
	/**
	 * <span lang="ja">先ほどの Number3d に、この Number3d の値をコピーします。</span>
	 * <span lang="en">Copies the values of this Number3d to the passed Number3d.</span>
	 * 
	 */
	public function copyTo(n:Number3D):void
	{
		n.x = x;
		n.y = y;
		n.z = z;
	}	
	/**
	 * <span lang="ja">先ほどの Number3d に、この Number3d の値をコピーします。</span>
	 * <span lang="en">Copies the values of this Number3d to the passed Number3d.</span>
	 * 
	 */
	public function copyFrom(n:Number3D):void
	{
		x = n.x; 
		y = n.y; 
		z = n.z; 
	}
	
	/** 
	 * <span lang="ja">Number3D のプロパティを設定する簡易な方法です。</span>
	 * <span lang="en">Quick way to set the properties of the Number3D</span>
	 * 
	 */
	public function reset(newx:Number = 0, newy:Number = 0, newz:Number = 0):void
	{
		x = newx; 
		y = newy; 
		z = newz; 
	}

	// ______________________________________________________________________ MATH

	/**
	* モジュロ
	* 
	*/

	public function get modulo() : Number
	{
		return Math.sqrt( this.x*this.x + this.y*this.y + this.z*this.z );
	}

	/**
	* 追加
	* 
	*/
	public static function add( v:Number3D, w:Number3D ):Number3D
	{
		return new Number3D
		(
			v.x + w.x,
			v.y + w.y,
			v.z + w.z
		);
	}

	/**
	 * <span lang="ja">控除</span>
	 * <span lang="en">Subtract.</span>
	 */
	public static function sub( v:Number3D, w:Number3D ):Number3D
	{
		return new Number3D
		(
			v.x - w.x,
			v.y - w.y,
			v.z - w.z
		);
	}

	/**
	 * <span lang="ja">ドット積</span>
	 * <span lang="en">Dot product.</span>
	 */
	public static function dot( v:Number3D, w:Number3D ):Number
	{
		return ( v.x * w.x + v.y * w.y + w.z * v.z );
	}

	/**
	 * <span lang="ja">外積です。置き換えるために、ターゲットの Number3D を任意にとります。
	 * なぜなら、いつも新しい numer3D を作成する訳ではないからです。
	 * crossEq 関数を作成しますか？</span>
	 * 
	 * <span lang="en">Cross product. Now optionally takes a target Number3D to put the change into. 
	 * So we're not constantly making new number3Ds. 
	 * Maybe make a crossEq function? </span>
	 */
	public static function cross( v:Number3D, w:Number3D, targetN:Number3D = null ):Number3D
	{
		if(!targetN) targetN = ZERO; 
		 
		targetN.reset((w.y * v.z) - (w.z * v.y), (w.z * v.x) - (w.x * v.z), (w.x * v.y) - (w.y * v.x));
		return targetN; 
	}



	/**
	 * 正規化
	 * 
	 */
	public function normalize():void
	{
		var mod:Number = Math.sqrt( this.x*this.x + this.y*this.y + this.z*this.z );

		if( mod != 0 && mod != 1)
		{
			mod = 1 / mod; // mults are cheaper then divs
			this.x *= mod;
			this.y *= mod;
			this.z *= mod;
		}
	}
	/**
	 * <span lang="ja">number によってベクターを乗算します。*= 演算子と同じです。</span>
	 * <span lang="en">Multiplies the vector by a number. The same as the *= operator</span>
	 */
	public function multiplyEq(n:Number):void
	{
		x*=n; 
		y*=n;
		z*=n; 	
	}
	
	/**
	 * <span lang="ja">先ほどのベクターをこのベクターに追加する。+= 演算子と同じです。</span>
	 * <span lang="en">Adds the vector passed to this vector. The same as the += operator. </span>
	 */
	public function plusEq(v:Number3D):void
	{
		x+=v.x; 
		y+=v.y; 
		z+=v.z; 	
	}
	
	/**
	 * <span lang="ja">先ほどのベクターをこのベクターに控除します。-=演算子と同じです。</span>
	 * <span lang="en">Subtracts the vector passed to this vector. The same as the -= operator. </span>
	 */
	
	public function minusEq(v:Number3D):void
	{
		x -= v.x; 
		y -= v.y; 
		z -= v.z; 	
		
	}

	// ______________________________________________________________________
	
	/**
	 * <span lang="ja">高速にモジュロの長さ、大きさを比較します。</span>
	 * <span lang="en">Super fast modulo(length, magnitude) comparisons.</span>
	 * 
	 *  
	 */
	public function isModuloLessThan(v:Number):Boolean
	{
			
		return (moduloSquared<(v*v)); 
		
	}
	
	public function isModuloGreaterThan(v:Number):Boolean
	{
			
		return (moduloSquared>(v*v)); 
		
	}
	public function isModuloEqualTo(v:Number):Boolean
	{
			
		return (moduloSquared==(v*v)); 
		
	}
		
	public function get moduloSquared():Number
	{
		return ( this.x*this.x + this.y*this.y + this.z*this.z );
	}
	
	
	// ______________________________________________________________________



	/**
	* <span lang="ja">x,y,z プロパティをゼロに設定して Number3D オブジェクトを取得します。</span>
	* <span lang="en">Returns a Number3D object with x, y and z properties set to zero.</span>
	*
	* @return A Number3D object.
	*/
	static public function get ZERO():Number3D
	{
		return new Number3D( 0, 0, 0 );
	}


	/**
	* <span lang="ja">指定の Number3D オブジェクトの3次元の値を表わす string 値を取得します。</span>
	* <span lang="en">Returns a string value representing the three-dimensional values in the specified Number3D object.</span>
	*
	* @return	A string.
	*/
	public function toString(): String
	{
		return 'x:' + Math.round(x*100)/100 + ' y:' + Math.round(y*100)/100 + ' z:' + Math.round(z*100)/100;		
	
	}
	
	//------- TRIG FUNCTIONS
	
	/**
	 * 
	 * 
	 * 
	 */
	
	public function rotateX(angle:Number) :void
	{
		if(Papervision3D.useDEGREES) angle*= toRADIANS; 
		
		var cosRY:Number = Math.cos(angle);
		var sinRY:Number = Math.sin(angle);

		temp.copyFrom(this); 

		this.y = (temp.y*cosRY)-(temp.z*sinRY);
		this.z = (temp.y*sinRY)+(temp.z*cosRY);
		
	}	

	public function rotateY(angle:Number) :void
	{
		
		if(Papervision3D.useDEGREES) angle*= toRADIANS; 
		
		var cosRY:Number = Math.cos(angle);
		var sinRY:Number = Math.sin(angle);

		temp.copyFrom(this); 
		
		this.x= (temp.x*cosRY)+(temp.z*sinRY);
		this.z= (temp.x*-sinRY)+(temp.z*cosRY);
		
		
	}	
	public function rotateZ(angle:Number) :void
	{
		
		if(Papervision3D.useDEGREES) angle*= toRADIANS; 

		var cosRY:Number = Math.cos(angle);
		var sinRY:Number = Math.sin(angle);

		temp.copyFrom(this); 		
		//this.x= temp.x;
		this.x= (temp.x*cosRY)-(temp.y*sinRY);
		this.y= (temp.x*sinRY)+(temp.y*cosRY);
		
		
	}
}
}
